/**
 * Copyright 2007 Igor Chaves Cananéa
 *
 * This file is part of PBSFW
 *
 * PBSFW is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * PBSFW is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "resource.h"

#define _INTERNAL_CLASS_
#include <cstdarg>
#include "process.h"
#include "simevent.h"
#include "simulator.h"
#include "deleter.h"
#include "task.h"
using namespace std;

namespace pbsfw {
    uint32_t Process::nextId = 0;

    Process::Process(string name)
        : id(Process::getNextId()), active(false), dead(true), name(name),
            nextTask(0), eventsFired(*new SimEventVector()),
            eventsWaiting(*new SimEventVector()), canDelete(true) {}

    void Process::entry(void *args) {
        Process *p = reinterpret_cast<Process*>(args);
        p->run();
        p->decRef();
        p->canDelete = false;
        p->dead = true;
        p->active = false;
        if (p->refCount() <= 0) {
            p->put(*Deleter::toDelete, p);
        }
        co_exit();
    }

    void Process::hold(double howMuch) {
        sim.hold(*this, howMuch);
    }

    void Process::passivate() {
        sim.passivate(*this);
    }

    void Process::waitfor(SimEvent *event) {
        this->eventsFired.clear();
        this->removeFromEventWaitQueue();
        this->eventsWaiting.clear();

        if (!event->hasOccurred()) {
            event->wait(*this);
            this->eventsWaiting.push_back(event);
            this->passivate();
        }
        else {
            this->addFiredEvent(*event);
            event->clear();
        }
    }

    bool Process::do_waitfor(uint qntEvents, va_list ap) {
        SimEvent **events = new SimEvent*[qntEvents];

        bool occurred = false;
        for (uint i = 0; i < qntEvents; ++i) {
            SimEvent *e = va_arg(ap, SimEvent*);
            events[i] = e;
            occurred = occurred || e->hasOccurred();
        }

        bool ret = false;
        if (occurred) {
            for (uint i = 0; i < qntEvents; ++i) {
                SimEvent *e = events[i];
                if (e->hasOccurred()) {
                    this->addFiredEvent(*e);
                    e->clear();
                }
            }
        }
        else {
            for (uint i = 0; i < qntEvents; ++i) {
                SimEvent *e = events[i];
                e->wait(*this);
                this->eventsWaiting.push_back(e);
            }
            ret = true;
        }
        delete [] events;

        return ret;
    }

    void Process::waitfor(uint qntEvents, ...) {
        this->eventsFired.clear();
        this->removeFromEventWaitQueue();
        this->eventsWaiting.clear();

        va_list ap;
        va_start(ap, qntEvents);
        bool willWait = this->do_waitfor(qntEvents, ap);
        va_end(ap);

        if (willWait) {
            this->passivate();
        }

    }

    SimEventVector Process::getFiredEvents() {
        return this->eventsFired;
    }

    void Process::request(Resource &r) {
        if (!r.request(*this)) {
            this->passivate();
        }
    }

    void Process::release(Resource &r) {
        r.release();
    }
};
